﻿// MIT License
// 开源地址：https://gitee.com/co1024/AbcMvc
// Copyright (c) 2021-2023 1024
// Abc.Mvc=Furion+EF+SqlSugar+Pear layui admin.

using Abc.Application;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.Extensions.Hosting;
using System;
using System.IO;

namespace Abc.Web.Core;

public class Startup : AppStartup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddConfigurableOptions<AppInfoOptions>(); //选项不同于配置，需在应用启动时注册
        services.AddConfigurableOptions<DbSettingOptions>();
        services.AddAbcMvcInit(); //ABC内置的

        services.AddFileLogging(options =>
        {
            //启用备用日志文件功能，也就是如果文件被占用了，可以创建新的备用日志继续写入，推荐！！！
            options.HandleWriteError = writeError =>
            {
                writeError.UseRollbackFileName(Path.GetFileNameWithoutExtension(writeError.CurrentFileName) + "-oops" +
                                               Path.GetExtension(writeError.CurrentFileName));
            };
        });

        #region 分布式缓存 SqlServer,其它方式后面在加

        //注册分布式数据库缓存
        //services.AddDistributedSqlServerCache(options =>
        //{
        //    options.ConnectionString = Configuration.GetConnectionString("DbCacheConnectionString");
        //    options.SchemaName = "dbo";
        //    options.TableName = "sys_DataCache";
        //    options.SystemClock = new SystemClockEx();
        //});

        #endregion 分布式缓存 SqlServer,其它方式后面在加

        //如果使用 SqlSugar 需要注册此服务
        services.AddSqlsugarSetup(App.Configuration);

        #region ActionFilter 授权验证

        services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            //后台
            .AddCookie(AuthorizeType.AdminAuthenticationScheme, options =>
            {
                //options.AccessDeniedPath = "/Account/Forbidden";
                options.LoginPath = "/HtAdmin/Login/Index";
                options.LogoutPath = "/HtAdmin/Login/LoginOut";
            });

        #endregion ActionFilter 授权验证


        // 默认授权机制，需授权的即可（方法）需贴 `[Authorize]` 特性
        //services.AddJwt<JwtHandler>(enableGlobalAuthorize: true);//必须在 .AddControllers() 之前注册！！

        services.AddCorsAccessor(); //添加跨域配置
        services.AddSession(options =>
        {
            options.IdleTimeout = TimeSpan.FromDays(1); // TimeSpan.FromMinutes(30);
        }); //Session

        services.Configure<RazorViewEngineOptions>(options =>
        {
            //自定视图文件，添加视图子文件夹的情况下很有用
            // 区域
            // 添加自定义视图路径方法
            // Areas/HtAdmin/Views/ERL/Article/Index.cshtml
            // Areas/区域名称/Views/文件夹名称/控制器Controller/方法Action.cshtml
            // Areas/{2}/Views/ERL/{1}/{0}.cshtml
            options.AreaViewLocationFormats.Add("~/Areas/{2}/Views/Defaults/{1}/{0}" + RazorViewEngine.ViewExtension);
            options.AreaViewLocationFormats.Add("~/Areas/{2}/Views/Devs/{1}/{0}" + RazorViewEngine.ViewExtension);
            options.AreaViewLocationFormats.Add("~/Areas/{2}/Views/Syss/{1}/{0}" + RazorViewEngine.ViewExtension);
            options.AreaViewLocationFormats.Add("~/Areas/{2}/Views/Manager/{1}/{0}" + RazorViewEngine.ViewExtension);
            // 非区域 添加自定义视图路径方法 /Views/ERL/Article/Index.cshtml
            // /Views/文件夹名称/控制器Controller/方法Action.cshtml /Views/ERL/{1}/{0}.cshtml options.ViewLocationFormats.Add("~/Views/ERL/{0}"+RazorViewEngine.ViewExtension);
        });

        services.AddRazorPages();
        services.AddControllersWithViews(options =>
            {
                //Web 拦截器
                //options.Filters.Add(new GlobalLogFilter());
                ////此处注册全局 输入验证
                //options.Filters.Add(new ValidateEntityFilterAttribute(ValidationEntityType.All, isMvcReutrnMultipleErrors: true));
                //此处 注册全局   防止重复提交 特性(影响性能,有1~10毫秒左右影响)
                options.Filters.Add(new PreventSpamFilterAttribute(httpAccessInterval: 1000));
                options.Filters.Add(new XSSFilterAttribute()); //xss攻击防御
            })
            .AddAbcJsonOptions() //system.text.json
            //.AddAbcNewtonsoftJson()//newtonsoft.json 这两个使用一个就好了
            .AddAppLocalization() // 注册多语言
            .AddInjectWithUnifyResult(options => { });

        //对于获取客户端 IP 的场景只需要指定 ForwardedHeaders.XForwardedFor
        services.Configure<ForwardedHeadersOptions>(options =>
        {
            options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
            //清除已知网络和已知代理的默认列表
            options.KnownNetworks.Clear();
            options.KnownProxies.Clear();
        });
        // 添加实时通讯
        services.AddSignalR();

        //滑块验证码
        services.AddDistributedMemoryCache();
        services.AddSlideCaptcha(App.Configuration);


        //图形验证码
        services.AddCaptcha(App.Configuration);

        //, option =>
        //  {
        //      option.CaptchaType = CaptchaType.WORD; // 验证码类型
        //      option.CodeLength = 4; // 验证码长度, 要放在CaptchaType设置后.  当类型为算术表达式时，长度代表操作的个数
        //      option.ExpirySeconds = 30; // 验证码过期时间
        //      option.IgnoreCase = true; // 比较时是否忽略大小写
        //      option.StoreageKeyPrefix = ""; // 存储键前缀

        //      option.ImageOption.Animation = true; // 是否启用动画
        //      option.ImageOption.FrameDelay = 30; // 每帧延迟,Animation=true时有效, 默认30

        //      option.ImageOption.Width = 150; // 验证码宽度
        //      option.ImageOption.Height = 50; // 验证码高度
        //      option.ImageOption.BackgroundColor = SkiaSharp.SKColor.Parse("#fff"); // 验证码背景色

        //      option.ImageOption.BubbleCount = 2; // 气泡数量
        //      option.ImageOption.BubbleMinRadius = 5; // 气泡最小半径
        //      option.ImageOption.BubbleMaxRadius = 15; // 气泡最大半径
        //      option.ImageOption.BubbleThickness = 1; // 气泡边沿厚度

        //      option.ImageOption.InterferenceLineCount = 2; // 干扰线数量

        //      option.ImageOption.FontSize = 36; // 字体大小
        //      option.ImageOption.FontFamily = DefaultFontFamilys.Instance.Actionj; // 字体

        //      /* 
        //       * 中文使用kaiti，其他字符可根据喜好设置（可能部分转字符会出现绘制不出的情况）。
        //       * 当验证码类型为“ARITHMETIC”时，不要使用“Ransom”字体。（运算符和等号绘制不出来）
        //       */

        //      option.ImageOption.TextBold = true;// 粗体，该配置2.0.3新增
        //  }


        //防止跨站点请请求伪造xsrf/csrf
        services.AddCSRF();

        services.AddSchedule(); //添加任务调度服务

        #region 远程服务

        services.AddHttpRemote(); //注册远程请求服务

        #endregion 远程服务

        services.AddMvcFilter<LoggingMonitorAttribute>();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            //添加 Forwarded Headers 中间件
            app.UseForwardedHeaders();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            //添加 Forwarded Headers 中间件
            app.UseForwardedHeaders();
            app.UseHsts();
        }

        // 添加规范化结果状态码，需要在这里注册
        app.UseUnifyResultStatusCodes();
        // 配置多语言，必须在 路由注册之前
        app.UseAppLocalization();

        app.UseHttpsRedirection();
        app.UseCustomStaticFile();
        app.UseStaticFiles();

        //app.UseSerilogRequestLogging();    // 必须在 UseStaticFiles 和 UseRouting 之间
        app.UseRouting();
        app.UseCorsAccessor();
        app.UseSession();
        app.UseAuthentication();
        app.UseAuthorization();

        app.UseInject();

        //原生的Context中并没有包含客户端的IP地址跟端口号。
        //我们需要手动的在Nuget中引入
        //"Microsoft.AspNetCore.HttpOverrides": "1.0.0" 这个包
        app.UseForwardedHeaders(new ForwardedHeadersOptions
        {
            ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
        });

        app.UseEndpoints(endpoints =>
        {
            // 批量注册集线器
            endpoints.MapHubs();
            endpoints.MapRazorPages();

            endpoints.MapAreaControllerRoute(
                "areas", "areas",
                "{area:exists}/{controller=Home}/{action=Index}/{id?}");

            endpoints.MapControllerRoute(
                "areas",
                "{area:exists}/{controller=Home}/{action=Index}/{id?}"
            );
            endpoints.MapControllerRoute(
                "default",
                "{controller=Home}/{action=Index}/{id?}");
        });

        ApplicationInit.Init();
    }
}